/**
 * @file Fourier_transform.cpp
 * @author enemy1205 (enemy1205@qq.com)
 * @brief 傅里叶变换头类
 * @date 2021-09-18
 */
#include "Fourier_transform.h"

/**
 * @brief 构造函数(主函数)
 * @param img 输入待处理图像
 */
using namespace cv;
FourierTransform::FourierTransform(Mat &img) {
    this->src = img;
    imshow("origin", img);
    cvtColor(src, fImg, COLOR_BGR2GRAY);
    fImg.convertTo(fImg, CV_64FC1);
    this->ImageDFT();
    //傅里叶逆变换
    cv::dft(fftImage, res, DFT_INVERSE + DFT_REAL_OUTPUT + DFT_SCALE);
    Mat planes[2];
    split(fftImage, planes);
//    cout << fftImage.channels() << endl;
    magnitude(planes[0], planes[1], planes[0]);//将复数转换为幅值M
    Mat magnitudeImage = planes[0];
    magnitudeImage += Scalar::all(1);     // 所有的像素都加1
    log(magnitudeImage, magnitudeImage);  // 求自然对数,进行对数尺度(logarithmic scale)缩放
    normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);//归一化
    magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols & -2, magnitudeImage.rows & -2));//若有奇数行或奇数列，进行频谱裁剪
    this->resort(magnitudeImage);
    //裁剪傅里叶逆变换
    res = res(Rect(0, 0, src.cols, src.rows));
    res.convertTo(res, CV_8UC1);
    namedWindow("逆变换图", WINDOW_AUTOSIZE);
    imshow("逆变换图", res);
    waitKey(0);
}

/**
 * @brief 快速傅里叶变换
 */
void FourierTransform::ImageDFT() {
    //判断位深
    CV_Assert(fImg.type() == CV_32FC1 || fImg.type() == CV_64FC1);
    CV_Assert(fImg.channels() == 1 || fImg.channels() == 2);
    //快速傅里叶变换的计算速度与图像的大小有关，计算最优图像尺寸，然后进行扩充，扩充值为0
    Mat padded;
    int rPadded = getOptimalDFTSize(fImg.rows);
    int cPadded = getOptimalDFTSize(fImg.cols);
    copyMakeBorder(fImg, padded, 0, rPadded - fImg.rows, 0, cPadded - fImg.cols, BORDER_CONSTANT, Scalar::all(0));
    //快速的傅里叶变换（双通道：用于存储实部 和 虚部）
    dft(padded, this->fftImage, DFT_COMPLEX_OUTPUT);
}

/**
 * @brief 重新排列傅立叶图像中的象限，使得原点位于图像中心
 * @param magnitudeImage 复数图像
 */
void FourierTransform::resort(Mat &magnitudeImage) {
    int cx = magnitudeImage.cols / 2;
    int cy = magnitudeImage.rows / 2;
    Mat q0(magnitudeImage, Rect(0, 0, cx, cy));
    Mat q1(magnitudeImage, Rect(cx, 0, cx, cy));
    Mat q2(magnitudeImage, Rect(0, cy, cx, cy));
    Mat q3(magnitudeImage, Rect(cx, cy, cx, cy));
    //交换象限（左上与右下进行交换）
    Mat tmp;
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);
    //交换象限（右上与左下进行交换）
    q1.copyTo(tmp);
    q2.copyTo(q1);
    tmp.copyTo(q2);
    imshow("fft图", magnitudeImage);
}